#include "stdint.h"
#include "stdbool.h"
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "symbol.h"

struct parse_buffer {
	const char* content;
	size_t length;
	size_t offset;
};

#define can_read(buffer, size) ((NULL != buffer) && (((buffer)->offset + size) <= (buffer)->length))
#define can_access_at_index(buffer, index) ((NULL != buffer) && (((buffer)->offset + index) < (buffer)->length))
#define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index))
#define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset)


static symbol* new_item(void)
{
	symbol* s = malloc(sizeof(symbol));
	if (s) {
		memset(s, 0, sizeof(symbol));
	}

	return s;
}

static void del_item(symbol* item)
{
	if (item->child) {
		del_item(item->child);
	}

	if (item->next) {
		del_item(item->next);
	}

	if (item->string) {
		free(item->string);
	}
	
	struct symbol_value* v = item->value;
	while (v) {
		struct symbol_value* nv = v->next;

		if (v->v) {
			free(v->v);
		}

		free(v);

		v = nv;
	}

	free(item);
}

static symbol_value* new_value(void)
{
	symbol_value* v = malloc(sizeof(symbol_value));
	if (v) {
		memset(v, 0, sizeof(symbol_value));
	}

	return v;
}


static struct parse_buffer* skip_utf8_bom(struct parse_buffer* buffer) 
{
	if ((NULL == buffer) || (NULL == buffer->content) || (0 != buffer->offset)) {
		return NULL;
	}

	if (can_access_at_index(buffer, 4) && (strncmp(buffer_at_offset(buffer), "\xEF\xBB\xBF", 3) == 0)) {
		buffer->offset += 3;
	}

	return buffer;
}

static struct parse_buffer* buffer_skip_whitespace(struct parse_buffer* buffer)
{
	if ((NULL == buffer) || (NULL == buffer->content)) {
		return NULL;
	}

	if (cannot_access_at_index(buffer, 0)) {
		return buffer;
	}

	while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32)) {
		buffer->offset++;
	}

	if (buffer->offset == buffer->length) {
		buffer->offset--;
	}

	return buffer;
}

static char* parse_string(struct parse_buffer* buffer)
{
	char* output;
	const char* end;
	size_t output_s;

	buffer_skip_whitespace(buffer);

	output = NULL;
	output_s = 0;
	end = buffer_at_offset(buffer);

	while (((size_t)(end - buffer->content) < buffer->length) && (*end > 32)) {
		end++;
	}

	if (((size_t)(end - buffer->content) >= buffer->length) || (*end > 32)) {
		goto fail;
	}

	output_s = (size_t)(end - buffer_at_offset(buffer)) + sizeof("");
	output = malloc(output_s);
	if (NULL == output) {
		goto fail;
	}

	memset(output, '\0', output_s);
	output_s = (size_t)(end - buffer_at_offset(buffer));
	memcpy(output, buffer_at_offset(buffer), output_s);
	buffer->offset += output_s;

	return output;

fail:
	if (output) {
		free(output);
	}
	return NULL;
}

static bool parse_object(symbol* item, struct parse_buffer* buffer)
{
	symbol* head = NULL;
	symbol* current_item = NULL;

	if ((NULL == item) || (NULL == buffer) || (NULL == buffer->content)) {
		return false;
	}

	if (cannot_access_at_index(buffer, 0) || (buffer_at_offset(buffer)[0] != '(')) {
		goto fail;
	}

	buffer->offset++;
	buffer_skip_whitespace(buffer);
	if (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] == ')')) {
		goto success;
	}

	item->string = parse_string(buffer);
	if (NULL == item->string) {
		goto fail;
	}

	buffer_skip_whitespace(buffer);
	if (can_access_at_index(buffer, 0)) {
		if (buffer_at_offset(buffer)[0] == ')') {
			goto success;
		}
	}
	else {
		goto fail;
	}

	do {
		buffer_skip_whitespace(buffer);

		if (buffer_at_offset(buffer)[0] == '(') {
			/* objext */
			symbol* nitem = NULL;

			nitem = new_item();
			if (NULL == nitem) {
				goto fail;
			}

			if (parse_object(nitem, buffer)) {
				symbol** c = &item->child;
				while (*c) {
					c = &(*c)->next;
				}

				*c = nitem;
			}
			else
			{
				del_item(nitem);
				goto fail;
			}
		}
		else {
			/* value */
			char* s = NULL;
			s = parse_string(buffer);
			if (s) {
				symbol_value** v = &item->value;
				while (*v) {
					v = &(*v)->next;
				}

				*v = new_value();
				if (*v) {
					(*v)->v = s;
				}
				else {
					free(s);
					goto fail;
				}
			}
			else {
				goto fail;
			}
		}
	} while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] != ')'));

success:
	return true;

fail:
	return false;
}



symbol* symbol_parse(char* value, size_t value_length)
{
	struct parse_buffer buffer = {0};
	symbol* item = NULL;

	buffer.content = value;
	buffer.length = value_length;
	buffer.offset = 0;

	item = new_item();
	if (NULL == item) {
		goto fail;
	}

	if (!parse_object(item, buffer_skip_whitespace(skip_utf8_bom(&buffer)))) {
		goto fail;
	}

	return item;

fail:
	del_item(item);
	return NULL;
}



void main(void)
{


}


